using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Threading;
using System.Threading.Tasks;
using ICSharpCode.TextEditor.Util;

namespace ICSharpCode.TextEditor.Document
{
    public class GuidelinesManager : IGuidelinesManager
    {
        private int offsetWidth = 3;

        private bool redrawable = false;

        private bool linesIsUpdating = false;

        private bool threedSynchCompleted = false;

        private char openedChar = '{';

        private char closedChar = '}';

        private IDocument document = null;

        private LineManager lineManager = null;

        private ThreadSafeList<DrawLine> guidelines = new ThreadSafeList<DrawLine>();

        private Stack<Guideline> GuidelineStack = new Stack<Guideline>();

        public bool ShowGuidelines
        {
            get
            {
                if (document == null)
                {
                    return false;
                }
                return document.TextEditorProperties.ShowGuidelines;
            }
        }

        public bool Redrawable
        {
            get
            {
                return redrawable;
            }
            set
            {
                redrawable = value;
            }
        }

        private bool LineIsDrawable(Rectangle rect, Point start, Point end)
        {
            int top = rect.Top;
            int bottom = rect.Bottom;
            if (start.Y < top && end.Y < top)
            {
                return false;
            }
            if (start.Y > bottom && end.Y > bottom)
            {
                return false;
            }
            return true;
        }

        private char GetCharAtLineSegment(string text)
        {
            char c = '\0';
            checked
            {
                if (!string.IsNullOrEmpty(text))
                {
                    int num = text.IndexOf(openedChar);
                    int num2 = text.IndexOf(closedChar);
                    if (num != -1 && num2 != -1)
                    {
                        return c;
                    }
                    if (num == -1 && num2 == -1)
                    {
                        return c;
                    }
                    if (text.IndexOf("//") != -1)
                    {
                        text = text.Substring(0, text.IndexOf("//"));
                    }
                    if (string.IsNullOrEmpty(text))
                    {
                        return c;
                    }
                    char[] array = text.Trim().ToCharArray();
                    for (int i = 0; i < 1; i++)
                    {
                        if (array[i] == closedChar)
                        {
                            c = closedChar;
                            break;
                        }
                    }
                    if (c != closedChar)
                    {
                        for (int num3 = array.Length - 1; num3 >= 0; num3--)
                        {
                            if (array[num3] != ' ')
                            {
                                c = array[num3];
                                break;
                            }
                        }
                    }
                }
                return c;
            }
        }

        private Point GetScreenPosition(TextArea textArea, int line, int column, Point offset)
        {
            Point point = new Point(textArea.VirtualTop.X, textArea.VScrollBar.Value);
            int drawingXPos = textArea.TextView.GetDrawingXPos(line, column);
            return checked(new Point(textArea.TextView.DrawingPosition.X + drawingXPos + offset.X, textArea.TextView.DrawingPosition.Y + textArea.Document.GetVisibleLine(line) * textArea.TextView.FontHeight - point.Y + offset.Y));
        }

        private void UpdateGuidelines(TextArea textArea)
        {
            linesIsUpdating = true;
            if (!document.TextEditorProperties.ShowGuidelines)
            {
                return;
            }
            GuidelineStack.Clear();
            ThreadSafeList<DrawLine> threadSafeList = new ThreadSafeList<DrawLine>();
            if (!document.TextEditorProperties.ShowGuidelines)
            {
                return;
            }
            checked
            {
                for (int i = 0; i < lineManager.TotalNumberOfLines; i++)
                {
                    int textLength = document.TextLength;
                    LineSegment lineSegment = document.GetLineSegment(i);
                    if (!linesIsUpdating)
                    {
                        break;
                    }
                    int j;
                    for (j = lineSegment.Offset; j < textLength; j++)
                    {
                        char charAt;
                        if ((charAt = document.GetCharAt(j)) != ' ' && charAt != '\t')
                        {
                            break;
                        }
                    }
                    int num = j - lineSegment.Offset;
                    char charAtLineSegment = GetCharAtLineSegment(document.GetText(j, lineSegment.Length - num));
                    if (!linesIsUpdating)
                    {
                        break;
                    }
                    Guideline guideline = null;
                    if (charAtLineSegment == openedChar)
                    {
                        guideline = new Guideline();
                        guideline.LineNumber = i;
                        guideline.Location = document.OffsetToPosition(j);
                        GuidelineStack.Push(guideline);
                    }
                    else
                    {
                        if (charAtLineSegment != closedChar || GuidelineStack.Count <= 0)
                        {
                            continue;
                        }
                        TextLocation endLocation = document.OffsetToPosition(j);
                        guideline = GuidelineStack.Pop();
                        if (guideline != null && guideline.Location.Line < endLocation.Line - 1 && guideline.Location.Column == endLocation.Column)
                        {
                            DrawLine drawLine = new DrawLine(guideline.Location, endLocation);
                            drawLine.StartPoint = GetScreenPosition(textArea, guideline.Location.Line, guideline.Location.Column, new Point(offsetWidth, textArea.TextView.FontHeight + 2));
                            drawLine.EndPoint = GetScreenPosition(textArea, endLocation.Line, endLocation.Column, new Point(offsetWidth, -2));
                            if (drawLine.StartPoint.X == drawLine.EndPoint.X)
                            {
                                threadSafeList.Add(drawLine);
                            }
                        }
                    }
                }
                lock (guidelines.SynchRoot)
                {
                    guidelines = threadSafeList.CloneToList<DrawLine>();
                    threadSafeList.Dispose();
                }
                linesIsUpdating = false;
            }
        }

        private void UpdateDrawLines(TextArea textArea)
        {
            if (!document.TextEditorProperties.ShowGuidelines || !threedSynchCompleted || textArea == null)
            {
                return;
            }
            lock (guidelines.SynchRoot)
            {
                if (!document.TextEditorProperties.ShowGuidelines || !threedSynchCompleted || textArea == null)
                {
                    return;
                }
                linesIsUpdating = true;
                foreach (DrawLine guideline in guidelines)
                {
                    if (!threedSynchCompleted || !linesIsUpdating)
                    {
                        break;
                    }
                    guideline.StartPoint = GetScreenPosition(textArea, guideline.StartLocation.Line, guideline.StartLocation.Column, new Point(offsetWidth, checked(textArea.TextView.FontHeight + 2)));
                    if (!threedSynchCompleted || !linesIsUpdating)
                    {
                        break;
                    }
                    guideline.EndPoint = GetScreenPosition(textArea, guideline.EndLocation.Line, guideline.EndLocation.Column, new Point(offsetWidth, -2));
                }
                linesIsUpdating = false;
            }
        }

        private void TextAreaInvalidate(TextArea textArea)
        {
            if (redrawable)
            {
                textArea.Invalidate();
            }
            else
            {
                textArea.Invalidate(textArea.TextView.DrawingPosition);
            }
        }

        internal GuidelinesManager(IDocument document, LineManager lineManager)
        {
            this.document = document;
            this.lineManager = lineManager;
        }

        internal virtual void Render(Graphics g, TextArea textArea)
        {
            if (linesIsUpdating || !document.TextEditorProperties.ShowGuidelines)
            {
                return;
            }
            Rectangle drawingPosition = textArea.TextView.DrawingPosition;
            using (Pen pen = new Pen(Brushes.Gray))
            {
                pen.DashStyle = DashStyle.DashDot;
                foreach (DrawLine guideline in guidelines)
                {
                    if (!document.TextEditorProperties.EnableFolding || document.FoldingManager.IsLineVisibleFast(guideline.StartLocation.Line))
                    {
                        Point screenPosition = GetScreenPosition(textArea, guideline.StartLocation.Line, guideline.StartLocation.Column, new Point(offsetWidth, checked(textArea.TextView.FontHeight + 2)));
                        Point screenPosition2 = GetScreenPosition(textArea, guideline.EndLocation.Line, guideline.EndLocation.Column, new Point(offsetWidth, -2));
                        if (LineIsDrawable(drawingPosition, screenPosition, screenPosition2))
                        {
                            guideline.Draw(g, screenPosition, screenPosition2, pen);
                        }
                    }
                }
            }
        }

        internal Task<bool> AsyncUpdateDrawLinesTask(TextArea textArea)
        {
            TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
            new Task(delegate
            {
                Thread.Sleep(15);
                UpdateDrawLines(textArea);
                tcs.SetResult(result: true);
            }).Start();
            return tcs.Task;
        }

        internal Task<bool> AsyncUpdateGuidelinesTask(TextArea textArea)
        {
            TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
            new Task(delegate
            {
                UpdateGuidelines(textArea);
                tcs.SetResult(result: true);
            }).Start();
            return tcs.Task;
        }

        public void Update(TextArea textArea)
        {
            guidelines = new ThreadSafeList<DrawLine>();
            if (!document.TextEditorProperties.ShowGuidelines || (document.TextEditorProperties.SplitState && document.TextEditorProperties.EnableFolding) || textArea.Document.TotalNumberOfLines <= 0 || textArea.TextView == null)
            {
                return;
            }
            threedSynchCompleted = false;
            if (!linesIsUpdating)
            {
                offsetWidth = textArea.TextView.CurlyBracketsWidth;
                UpdateGuidelines(textArea);
                if (textArea.Document.TotalNumberOfLines > 0)
                {
                    TextAreaInvalidate(textArea);
                }
            }
            threedSynchCompleted = true;
        }

        public void Invalidate(TextArea textArea)
        {
           // bool flag = false;
            if (document.TextEditorProperties.ShowGuidelines && threedSynchCompleted)
            {
                linesIsUpdating = false;
                UpdateDrawLines(textArea);
                TextAreaInvalidate(textArea);
            }
        }

        public void Clear(TextArea textArea)
        {
            if (document.TextEditorProperties.ShowGuidelines)
            {
                threedSynchCompleted = false;
                guidelines.Clear();
                guidelines = new ThreadSafeList<DrawLine>();
                textArea.Invalidate(textArea.TextView.DrawingPosition);
            }
        }
    }
}
